package xyz.scootaloo.kami.server.service.impl

import io.vertx.core.http.HttpServerRequest
import io.vertx.kotlin.coroutines.await
import xyz.scootaloo.kami.server.controller.dto.AccountForm
import xyz.scootaloo.kami.server.model.AccessLevel
import xyz.scootaloo.kami.server.model.SysUser
import xyz.scootaloo.kami.server.model.UserPrincipal
import xyz.scootaloo.kami.server.model.dao.UserDAO
import xyz.scootaloo.kami.server.standard.UserExistsException
import xyz.scootaloo.kami.server.standard.UserNotExistsException
import xyz.scootaloo.kami.server.standard.UserPasswordMistakeException
import xyz.scootaloo.kami.server.standard.getLogger
import xyz.scootaloo.kami.server.service.AccountService
import xyz.scootaloo.kami.server.service.HttpMessageHelper
import xyz.scootaloo.kami.server.service.JwtService
import xyz.scootaloo.kami.server.service.TokenService

/**
 * @author flutterdash@qq.com
 * @since 2022/3/22 12:05
 */
object InternalAccountServiceImpl : AccountService {

    private val log by lazy { getLogger() }

    private val tokenService = TokenService()
    private val jwtService = JwtService()

    override suspend fun register(request: HttpServerRequest, form: AccountForm): String {
        assertUserNotExists(form.username)
        return doRegister(request, form).apply {
            log.info("用户`${form.username}`注册成功")
        }
    }

    override suspend fun login(request: HttpServerRequest, form: AccountForm): String {
        val dbUser = assertUserExists(form.username)
        return doLogin(request, dbUser, form).apply {
            log.info("用户`${form.username}`登录成功")
        }
    }

    override suspend fun logout() {
        TODO("Not yet implemented")
    }

    private suspend fun doLogin(
        request: HttpServerRequest, dbUser: SysUser, form: AccountForm
    ): String {
        val realPass = decodePass(request, form)
        if (!isSamePass(realPass, dbUser))
            throw UserPasswordMistakeException()
        return issueJwtForUser(dbUser)
    }

    private suspend fun doRegister(request: HttpServerRequest, form: AccountForm): String {
        val realPass = decodePass(request, form)
        val salt = tokenService.generateRandomString()
        val dbUser = createNewUser(form.username, realPass, salt)
        UserDAO.store(dbUser)
        return issueJwtForUser(dbUser)
    }

    private suspend fun decodePass(request: HttpServerRequest, form: AccountForm): String {
        val ipAddress = HttpMessageHelper.getIpAddress(request)
        return tokenService.decodeContent(ipAddress, form.appId, form.password).await()
    }

    private fun createNewUser(username: String, realPass: String, sa: String) = SysUser {
        this.username = username
        this.password = tokenService.encodePasswordBySalt(realPass, sa)
        this.salt = sa
        this.role = AccessLevel.USER
    }

    private fun isSamePass(password: String, dbUser: SysUser): Boolean {
        return tokenService.matchPassword(password, dbUser.password, dbUser.salt)
    }

    private fun issueJwtForUser(dbUser: SysUser): String = jwtService.issueJWT(
        UserPrincipal(
            dbUser.id, dbUser.username, dbUser.role
        )
    )

    private fun assertUserExists(username: String): SysUser {
        return UserDAO.findByName(username) ?: throw UserNotExistsException()
    }

    private fun assertUserNotExists(username: String) {
        val dbUser = UserDAO.findByName(username)
        if (dbUser != null)
            throw UserExistsException()
    }
}